
//Namespace
package com.katujo.squirrel;

//Java imports
import java.lang.reflect.Field;
import java.util.Map;
import java.util.Set;

//Katujo imports
import com.katujo.squirrel.annotations.EscapeCharacter;
import com.katujo.squirrel.annotations.Table;
import com.katujo.squirrel.annotations.TablePattern;

/**
 * Write the SQL used by Squirrel.
 * 
 * <br/><br/>
 * Copyright 2010 Katujo LTD<br/>
 * Licensed under the Apache License, Version 2.0<br/>
 * (the "License"); you may not use this file except in compliance with the License. You may<br/>
 * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required <br/>
 * by applicable law or agreed to in writing, software distributed under the License is distributed <br/>
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either <br/>
 * express or implied. See the License for the specific language governing permissions and <br/>
 * limitations under the License. <br/>
 * 
 * @author Johan Hertz
 *
 */
public class SquirrelWriter
{
	
	/**
	 * Write the insert SQL for a class used by Squirrel.
	 * @param manager
	 * @param clazz
	 * @param fields
	 * @param columns
	 * @param autoIncrementField
	 * @return
	 */
	public static String writeInsert(SquirrelManager manager, Class<?> clazz, Set<Field> fields, Map<Field, String> columns, Field autoIncrementField) 
	{
		//Create the string builder
		StringBuilder builder = new StringBuilder();
		
		//Create the escape character
		String escapeCharacter = "";
		
		//Get the escape character annotation		
		EscapeCharacter escapeCharacterAnno  = manager.getClass().getAnnotation(EscapeCharacter.class);
		
		//Set the escape character
		if(escapeCharacterAnno != null)
			escapeCharacter = escapeCharacterAnno.character();		
		
		//Add insert
		builder.append("INSERT INTO ");
		
		//Add table name
		builder.append(escapeCharacter);
		builder.append(getTableName(manager, clazz));
		builder.append(escapeCharacter);
		builder.append(" \n");
					
		//Create the size
		int size = fields.size();
		
		//Remove one from the size if auto increment
		if(autoIncrementField != null)
			size--;
		
		//If there is no other fields then the auto field return as is
		if(size == 0)
			return builder.toString();
			
		//Create the counter
		int count = 0;
		
		//Add fields		
		for(Field field : fields)
		{
			//Don't add auto increment
			if(field != autoIncrementField)
			{				
				//Create the column name
				String columnName = columns.get(field);
				
				//First
				if(count == 0)
				{
					//Add the column name
					builder.append("\t(");
					builder.append(escapeCharacter);
					builder.append(columnName);
									
					//First and last
					if(size == 1)
					{
						builder.append(escapeCharacter);
						builder.append(")\n");
					}	
					
					//Not the last one
					else 
					{
						builder.append(escapeCharacter);
						builder.append(",\n");
					}
				}
							
				//Last
				else if(count == size-1)
				{
					builder.append("\t ");
					builder.append(escapeCharacter);
					builder.append(columnName);
					builder.append(escapeCharacter);
					builder.append(")\n");				
				}
				
				//Middle
				else
				{
					builder.append("\t ");
					builder.append(escapeCharacter);
					builder.append(columnName);
					builder.append(escapeCharacter);
					builder.append(",\n");					
				}
				
				//Add to the count
				count++;
			}
		}
		
		//Add the values
		while(count > 0)
		{
			//First
			if(count == size)
			{
				//Add VALUES key word
				builder.append("VALUES \n");
				
				//Add the column name
				builder.append("\t(?");
								
				//First and last
				if(size == 1)
					builder.append(")");
				
				//Not the last one
				else builder.append(", ");
			}
						
			//Last
			else if(count == 1)
				builder.append("?)");				
			
			//Middle
			else builder.append("?, ");					
			
			//Remove from the count
			count--;						
		}
		
		//Return the SQL string
		return builder.toString();
	}
	
	/**
	 * Write the update SQL for a class used by Squirrel.
	 * @param manager
	 * @param clazz
	 * @param noneIDFields
	 * @param idFields
	 * @return
	 */
	public static String writeUpdate(SquirrelManager manager, Class<?> clazz, Set<Field> noneIDFields, Set<Field> idFields, Map<Field, String> columns)
	{
		//Create the builder
		StringBuilder builder = new StringBuilder();
		
		//Create the escape character
		String escapeCharacter = "";
		
		//Get the escape character annotation		
		EscapeCharacter escapeCharacterAnno  = manager.getClass().getAnnotation(EscapeCharacter.class);
		
		//Set the escape character
		if(escapeCharacterAnno != null)
			escapeCharacter = escapeCharacterAnno.character();		
		
		//Don't write update if there is no ID fields
		if(idFields.size() == 0)
			return null;
		
		//Add UPDATE
		builder.append("UPDATE \n");
						
		//Add the table name
		builder.append("\t");
		builder.append(escapeCharacter);
		builder.append(getTableName(manager, clazz));
		builder.append(escapeCharacter);
		builder.append(" \n");
				
		//Add SET
		builder.append("SET \n");
			
		//Create the counter
		int count = 0;
		
		//Add the none ID fields		
		for(Field field : noneIDFields)
		{
			//Get the column name
			String columnName = columns.get(field);
			
			//Add field if last
			if(count == noneIDFields.size()-1)
			{	
				builder.append("\t");
				builder.append(escapeCharacter);
				builder.append(columnName);
				builder.append(escapeCharacter);
				builder.append(" = ? \n");
			}
			
			//Add field if not last
			else
			{
				builder.append("\t");
				builder.append(escapeCharacter);
				builder.append(columnName);
				builder.append(escapeCharacter);
				builder.append(" = ?, \n");				
			}
				
			//Add to the count
			count++;
		}
				
		//Add WHERE
		builder.append("WHERE \n");				
		
		//Set the count to 0
		count = 0;
		
		//Add the ID fields
		for(Field field : idFields)
		{
			//Get the column name
			String columnName = columns.get(field);	
				
			//Add if last
			if(count == idFields.size() -1)
			{
				builder.append("\t");
				builder.append(escapeCharacter);
				builder.append(columnName);
				builder.append(escapeCharacter);
				builder.append(" = ? ");				
			}
			
			//Add field if not last
			else
			{
				builder.append("\t");
				builder.append(escapeCharacter);
				builder.append(columnName);
				builder.append(escapeCharacter);
				builder.append(" = ? AND \n");									
			}			
			
			//Add to the count
			count++;			
		}
						
		//Return the builder string
		return builder.toString();		
	}
	
	/**
	 * Write the delete SQL for a class used by Squirrel.
	 * @param manager
	 * @param clazz
	 * @param idFields
	 * @return
	 */
	public static String writeDelete(SquirrelManager manager, Class<?> clazz, Set<Field> idFields, Map<Field, String> columns)
	{
		//Create the builder
		StringBuilder builder = new StringBuilder();
		
		//Create the escape character
		String escapeCharacter = "";
		
		//Get the escape character annotation		
		EscapeCharacter escapeCharacterAnno  = manager.getClass().getAnnotation(EscapeCharacter.class);
		
		//Set the escape character
		if(escapeCharacterAnno != null)
			escapeCharacter = escapeCharacterAnno.character();			
		
		//Don't write update if there is no ID fields
		if(idFields.size() == 0)
			return null;
		
		//Add UPDATE
		builder.append("DELETE FROM \n");
								
		//Add the table name if set
		builder.append("\t");
		builder.append(escapeCharacter);
		builder.append(getTableName(manager, clazz));
		builder.append(escapeCharacter);
		builder.append(" \n");		
		
		//Add WHERE
		builder.append("WHERE \n");				
		
		//Set the count to 0
		int count = 0;
					
		//Add the ID fields
		for(Field field : idFields)
		{
			//Get the column name
			String columnName = columns.get(field);
				
			//Add if last
			if(count == idFields.size() -1)
			{
				builder.append("\t");
				builder.append(escapeCharacter);
				builder.append(columnName);
				builder.append(escapeCharacter);
				builder.append(" = ? ");				
			}
			
			//Add field if not last
			else
			{
				builder.append("\t");
				builder.append(escapeCharacter);
				builder.append(columnName);
				builder.append(escapeCharacter);
				builder.append(" = ? AND \n");									
			}			
			
			//Add to the count
			count++;			
		}		
		
		//Return delete string
		return builder.toString();		
	}
	
	
	/**
	 * Write the select all SQL for a class used by Squirrel.
	 * @param manager
	 * @param clazz
	 * @param idFields
	 * @return
	 */
	public static String writeSelectAll(SquirrelManager manager, Class<?> clazz, Set<Field> fields, Map<Field, String> columns)
	{
		//Create the builder
		StringBuilder builder = new StringBuilder();
				
		//Don't write select if there is no fields
		if(fields.size() == 0)
			return null;
		
		//Create the escape character
		String escapeCharacter = "";
		
		//Get the escape character annotation		
		EscapeCharacter escapeCharacterAnno  = manager.getClass().getAnnotation(EscapeCharacter.class);
		
		//Set the escape character
		if(escapeCharacterAnno != null)
			escapeCharacter = escapeCharacterAnno.character();			
		
		//Add SELECT
		builder.append("SELECT \n");
		
		//Set the count to 0
		int count = 0;
				
		//Add the fields
		for(Field field : fields)
		{
			//Get the column name
			String columnName = columns.get(field);	
				
			//Add if last
			if(count == fields.size() -1)
			{
				builder.append("\t");
				builder.append(escapeCharacter);
				builder.append(columnName);
				builder.append(escapeCharacter);
				builder.append(" \n");				
			}
			
			//Add field if not last
			else
			{
				builder.append("\t");
				builder.append(escapeCharacter);
				builder.append(columnName);
				builder.append(escapeCharacter);
				builder.append(", \n");									
			}			
			
			//Add to the count
			count++;			
		}			
		
		//Add FROM
		builder.append("FROM \n");		
								
		//Add the table name
		builder.append("\t");
		builder.append(escapeCharacter);
		builder.append(getTableName(manager, clazz));
		builder.append(escapeCharacter);
		builder.append(" ");
				
		//Return delete string
		return builder.toString();		
	}	
	
	/**
	 * Write the select SQL (one instance) for a class used 
	 * by Squirrel.
	 * @param manager
	 * @param clazz
	 * @param fields
	 * @return
	 */
	public static String writeSelect(SquirrelManager manager, Class<?> clazz, Set<Field> fields, Set<Field> idFields, Map<Field, String> columns)
	{
		//Create the builder
		StringBuilder builder = new StringBuilder();
		
		//Don't write select if there is no fields
		if(fields.size() == 0)
			return null;
		
		//Create the escape character
		String escapeCharacter = "";
		
		//Get the escape character annotation		
		EscapeCharacter escapeCharacterAnno  = manager.getClass().getAnnotation(EscapeCharacter.class);
		
		//Set the escape character
		if(escapeCharacterAnno != null)
			escapeCharacter = escapeCharacterAnno.character();			
		
		//Add SELECT
		builder.append("SELECT \n");
		
		//Set the count to 0
		int count = 0;
					
		//Add the fields
		for(Field field : fields)
		{
			//Get the column name
			String columnName = columns.get(field);				
				
			//Add if last
			if(count == fields.size() -1)
			{
				builder.append("\t");
				builder.append(escapeCharacter);
				builder.append(columnName);
				builder.append(escapeCharacter);
				builder.append(" \n");				
			}
			
			//Add field if not last
			else
			{
				builder.append("\t");
				builder.append(escapeCharacter);
				builder.append(columnName);
				builder.append(escapeCharacter);
				builder.append(", \n");									
			}			
			
			//Add to the count
			count++;			
		}			
		
		//Add FROM
		builder.append("FROM \n");		
								
		//Add the table name if set
		builder.append("\t");
		builder.append(escapeCharacter);
		builder.append(getTableName(manager, clazz));
		builder.append(escapeCharacter);
		builder.append(" \n");
		
		//Add WHERE
		builder.append("WHERE \n");
		
		//Set the count to 0
		count = 0;		
		
		//Add the ID fields
		for(Field field : idFields)
		{
			//Get the column name
			String columnName = columns.get(field);
				
			//Add if last
			if(count == idFields.size() -1)
			{
				builder.append("\t");
				builder.append(escapeCharacter);
				builder.append(columnName);
				builder.append(escapeCharacter);
				builder.append(" = ? ");				
			}
			
			//Add field if not last
			else
			{
				builder.append("\t");
				builder.append(escapeCharacter);
				builder.append(columnName);
				builder.append(escapeCharacter);
				builder.append(" = ? AND \n");									
			}			
			
			//Add to the count
			count++;			
		}			
				
		//Return delete string
		return builder.toString();		
	}	
	
	/**
	 * Get the table name for the item.
	 * @param manager
	 * @param clazz
	 * @return
	 */
	public static String getTableName(SquirrelManager manager, Class<?> clazz)
	{		
		//Get the table annotation
		Table table = clazz.getAnnotation(Table.class);
		 
		 //Use table annotation if set
		 if(table != null)
			 return table.name();
		
		//Get the table pattern annotation
		TablePattern tablePattern = manager.getClass().getAnnotation(TablePattern.class);
		
		//Return the name as is
		if(tablePattern == null)
			return clazz.getSimpleName();
		
		//Get the name using the table pattern
		switch(tablePattern.pattern())
		{
			//Format the class name
			case JAVA: return clazz.getSimpleName();
			case UPPER_CASE: return upperPattern(clazz.getSimpleName());
			case LOWER_CASE: return lowerPattern(clazz.getSimpleName());
		
			//If the code gets to the line the code need to be updated.
			default:
				return null;
		}
	}
	
	/**
	 * Format a string using the java upper pattern
	 * @param str
	 * @return
	 */
	public static String javaUpperPattern(String str)
	{				
		//Create the string builder
		StringBuilder builder = new StringBuilder();
		
		//Get the array
		char[] array = str.toCharArray();
		
		//Add the chars to the builder
		for(int i=0; i<array.length; i++)
		{
			//Get the char
			char c = array[i];
			
			//Add first char as upper
			if(i == 0)
				builder.append(Character.toUpperCase(c));
									
			//Add as is
			else builder.append(c);			
		}
		
		//Return the formatted string
		return builder.toString();
	}
	
	/**
	 * Format a string using the upper pattern
	 * @param str
	 * @return
	 */
	public static String upperPattern(String str)
	{
		//Create the string builder
		StringBuilder builder = new StringBuilder();
		
		//Get the array
		char[] array = str.toCharArray();
		
		//Add the chars to the builder
		for(int i=0; i<array.length; i++)
		{
			//Get the char
			char c = array[i];
			
			//Add underscore on "new" upper case char
			if(i>0 && Character.isUpperCase(c) && !Character.isUpperCase(array[i-1]))
			{
				builder.append('_');
				builder.append(c);
			}
			
			//Add as upper
			else builder.append(Character.toUpperCase(c));			
		}
		
		//Return the formatted string
		return builder.toString();
	}
	
	/**
	 * Format a string using the upper pattern
	 * @param str
	 * @return
	 */
	public static String lowerPattern(String str)
	{	
		//Create the string builder
		StringBuilder builder = new StringBuilder();
		
		//Get the array
		char[] array = str.toCharArray();
		
		//Add the chars to the builder
		for(int i=0; i<array.length; i++)
		{
			//Get the char
			char c = array[i];
			
			//Add underscore on "new" upper case char
			if(i>0 && Character.isUpperCase(c) && !Character.isUpperCase(array[i-1]))
			{
				builder.append('_');
				builder.append(Character.toLowerCase(c));
			}
			
			//Add as lower
			else builder.append(Character.toLowerCase(c));			
		}
		
		//Return the formatted string
		return builder.toString();
	}
	


}
